package com.king.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/**
 * 懒汉式单例
 */
public class LazySingleton {
    //自定义属性值，可以防止都用反射破坏单例，但是不能被拿到king这个属性名
    private static boolean king = false;

    private LazySingleton(){
        //System.out.println(Thread.currentThread().getName()+"==OK");
        //synchronized可以避免getInstance后使用反射创建对象，无法避免都用反射破环
        synchronized (LazySingleton.class){
            if (king == false){
                king = true;
            }else {
                throw new RuntimeException("不要试图用反射破坏异常");
            }
            if (lazySingleton != null){
                throw new RuntimeException("不要试图用反射破坏异常");
            }
        }
    }

    /*private LazySingleton(){
        //System.out.println(Thread.currentThread().getName()+"==OK");
        //synchronized可以避免getInstance后使用反射创建对象，无法避免都用反射破环
        synchronized (LazySingleton.class){
            if (lazySingleton != null){
                throw new RuntimeException("不要试图用反射破坏异常");
            }
        }
    }*/

    //加上volatile避免指令重排
    private volatile static LazySingleton lazySingleton;

    /**
     * 单线程可以保证单例
     * 但是在多线程得情况下无法保证
     * @return
     */
    public static LazySingleton getInstance(){
        //解决多线程得问题，双重检测模式DCL
        if (lazySingleton == null){
            synchronized (LazySingleton.class){
                if (lazySingleton == null){
                    //不是一个原子性操作
                    //1、分配内存空间 2、执行构造方法，分配对象3、把对象指向空间
                  //有可能线程a 132  又来一个线程b 造成指令重排
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }

    //反射
    public static void main(String[] args) throws Exception {
//        for (int i = 0; i <10 ; i++) { new Thread(()->{ LazySingleton.getInstance(); }).start();}
        //LazySingleton lazySingleton = LazySingleton.getInstance();
        //通过反射获取king属性进行破坏
        Field king = LazySingleton.class.getDeclaredField("king");
        king.setAccessible(true);
        //通过反射获取构造器
        Constructor<LazySingleton> constructor = LazySingleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        LazySingleton lazySingleton1 = constructor.newInstance();
        king.set(lazySingleton1,false);
        LazySingleton lazySingleton2 = constructor.newInstance();
        System.out.println(lazySingleton1);
        System.out.println(lazySingleton2);
    }
}
